#include<iostream>
#include<fstream>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<pthread.h>
#include<stdlib.h>
#include<signal.h>
#include<arpa/inet.h>
#include<string.h>
#include<vector>

#define CRLF "\r\n"
#define SPACE " "
#define SPACELEN strlen(SPACE)
#define HOME_PAGE "index.html"
#define ROOT_PATH "./wwwroot"

using namespace std;

string getpath(string& httprequest)
{
    //提取第一行
    int pos=httprequest.find(CRLF);
    if(pos==string::npos)
    return "";
    string len=httprequest.substr(0,pos);
    //找到两个空格
    int spaceone=len.find(SPACE);
    if(spaceone==string::npos) return "";
    int spacetwo=len.rfind(SPACE);
    if(spacetwo==string::npos) return "";
    //提取路径
    string path=len.substr(spaceone+SPACELEN,spacetwo-(spaceone+SPACELEN));
    if(path.size()==1&&path[0]=='/')
    path+=HOME_PAGE;
    return path;
}

string getresource(string& path)
{
    ifstream in(path.c_str(),ifstream::binary);
    if(!in.is_open())
    {
        return "404";
    }
    string content;
    string line;
    while(getline(in,line))
    {
        content+=line;
    }
    in.close();
    return content;
}

void HandleRequest(int servicefd)
{
    char buffer[10240];
    ssize_t sz=read(servicefd,buffer,sizeof(buffer));
    if(sz>0)
    {
        cout<<"+++++++++++++++"<<endl;
        cout<<buffer<<endl;
    }
    //响应数据
    string buff(buffer);
    string path=ROOT_PATH;
    path+=getpath(buff);
    //cout<<path<<endl;
    string html = getresource(path);

    //string html="<html><h1>hello world</h1></html>\r\n";
    string response;

    //重定向
    // response="HTTP/1.0 302 Temporarily Moved\r\n";
    // response+="Location: https://www.qq.com/\r\n";
    // response+="\r\n";

    //状态码
    response="HTTP/1.0 200 OK\r\n";
    //报头
    response+="Content-Type: text/html\r\n";
    response+=("Content-Length: "+to_string(html.size())+"\r\n");
    response+="Set-Cookie: this my cookie content;\r\n";
    response+="\r\n";
    response+=html;

    //发送
    send(servicefd,response.c_str(),response.size(),0);
}

class tcp
{
public:
    tcp(uint16_t port,string ip="",int sockfd=-1):ip_(ip),port_(port)
    {

    }
    ~tcp()
    {

    }

    void init()
    {
        //1 创建服务器端
        listensockfd_=socket(AF_INET,SOCK_STREAM,0);
        if(listensockfd_==-1)
        {
            exit(1);
        }
        else
        {
        }
        //2 绑定网络信息
        //2.1 填充网络信息
        struct sockaddr_in local;
        local.sin_family=AF_INET;
        local.sin_port=htons(port_);
        local.sin_addr.s_addr=ip_.empty()?htons(INADDR_ANY):inet_addr(ip_.c_str());
        //2.2 bindsockaddr_in和套接字绑定
        if(bind(listensockfd_,(const sockaddr*)&local,(socklen_t)sizeof(local))==-1)
        {
            exit(2);
        }
        //3 将socket设置监听状态，因为tcp是面向连接的（面向连接：需要先建立连接，再进行工作）
        if(listen(listensockfd_,5)==-1)
        {
            exit(3);
        }
        //设置监听状态后，允许别人来连接你
    }
    void loop()
    {
        while(true)
        {
            // 1、获取连接，accept的返回值是一个新的sockfd
            // 传入的参数sockfd，这个套接字是用来获取新的连接（客户端来连接）
            // 返回值sockfd，这个套接字主要是为客户提供网络服务
            struct sockaddr_in peer;
            socklen_t len=sizeof(peer);
            int servicefd_ = accept(listensockfd_,(struct sockaddr*)&peer,&len);
            if(servicefd_==-1)
            {
                exit(1);
            }


            // 3、多进程版本
            // 爷爷进程获取连接，孙子进程提供服务，等待父进程，孙子进程被1号进程领养，自动等待
            int pid = fork();
            if(pid==0)//爸爸进程
            {
                close(listensockfd_);
                if(fork()>0)    
                {
                    exit(0);
                }
                HandleRequest(servicefd_);
                exit(0);
            }
            close(servicefd_);
            // 文件描述符泄露
            //孙子进程被bash收养，退出后，由系统自动回收，不会影响其他进程
            //爷爷进程调用waitpid时，爸爸进程肯定已经退出了，爷爷进程不会阻塞等待
            waitpid(pid,nullptr,0);
        }
    }
private:
    int listensockfd_;
    uint16_t port_;
    string ip_;

};


int main(int argc,char* argv[])
{
    if(argc!=2&&argc!=3)
    {
        cout<<"failed"<<endl;
        exit(3);
    }
    string ip;
    if(argc==3)
    {
        ip=argv[2];
    }
    uint16_t port=atoi(argv[1]);
    tcp svr(port,ip);
    svr.init();
    svr.loop();
    return 0;
}